package com.nipun.neural.utils;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;

import com.nipun.neural.objects.Network;
import com.nipun.neural.objects.Neuron;
import com.nipun.neural.objects.networks.NetworkImpl;
import com.nipun.neural.objects.neurons.InputNeuron;
import com.nipun.neural.objects.synapses.SynapseImpl;
import com.nipun.neural.utils.io.DiskIOUtils;

public class JordanNetworkConstructor {

	private int in,h;
	private String type,filePath;
	private Network net;
	public JordanNetworkConstructor(int inputs, int hidden, String type, String filePath){
		in=inputs;
		h=hidden;
		this.type=type;
		this.filePath=filePath;
		net = createNetwork();
		writeToDisk();
	}
	
	public Network createNetwork(){
		Network net = new NetworkImpl();
		Neuron[] inputs = new Neuron[in+1];
		for(int i=0; i<in+1; i++){
			inputs[i] = new InputNeuron("input"+i);
			net.addNode(inputs[i]);
		}
		Neuron output = getNeuronImpl("output", type);
		net.addNode(output);
		Neuron[] hidden = new Neuron[h];
		for(int i=0; i<h; i++){
			hidden[i] = getNeuronImpl("hidden"+i, type);
			net.addNode(hidden[i]);
		}
		//creating and adding synapses...
		
		for(int i=0; i<in+1; i++){
			for(int j=0; j<h; j++){
				net.addEdge(new SynapseImpl(inputs[i],hidden[j],1));
			}
		}
		
		for(int i=0; i<h; i++){
			net.addEdge(new SynapseImpl(hidden[i], output, 1));
		}
		
		List inodes = new ArrayList();
		List onodes = new ArrayList();
		List mnodes = new ArrayList();
		List forder = new ArrayList();
		for(int i=0; i<in; i++){
			inodes.add(inputs[i]);
		}
		for(int i=0; i<h; i++){
			forder.add(hidden[i]);
		}
		forder.add(output);
		
		onodes.add(output);
		
		mnodes.add(inputs[in]);
		
		//all objects created now add them to network
		net.setFiringOrder(forder);
		net.setInputNodes(inodes);
		net.setMemoryNodes(mnodes);
		net.setOutputNodes(onodes);
		
		return net;
	}
	
	public void writeToDisk(){
		DiskIOUtils dio = new DiskIOUtils();
		dio.writeNetworkToDisk(net, filePath);
	}
	
	
	private Neuron getNeuronImpl(String label, String type){
		if(type == null || "".equals(type)){
			return null;
		}else {
			Neuron neuronObj = null;
			Class neuronClass;
			Constructor constructor;
			try {
				neuronClass = Class.forName(getClassName(type));
				constructor = neuronClass.getConstructor(Class.forName("java.lang.String"));
				neuronObj = (Neuron) constructor.newInstance(label);
			} catch (ClassNotFoundException e) {
				e.printStackTrace();
			} catch (SecurityException e) {
				e.printStackTrace();
			} catch (NoSuchMethodException e) {
				e.printStackTrace();
			} catch (IllegalArgumentException e) {
				e.printStackTrace();
			} catch (InstantiationException e) {
				e.printStackTrace();
			} catch (IllegalAccessException e) {
				e.printStackTrace();
			} catch (InvocationTargetException e) {
				e.printStackTrace();
			}
			if(neuronObj == null){
				System.out.println("could not find a neuron of type :: " + type);
			}
			return neuronObj;
		}
	}
	
	private String getClassName(String type){
		PropertiesManager propManager = PropertiesManager.getInstance();
		Properties props = propManager.getProperties();
		return props.getProperty(type);
	}
}
